#ifndef _WIN32
#include <unistd.h>
#endif
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <syslog.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#include "thread_hal.h"
#include "libwebsockets.h"
#include "websocket_client.h"
#include "websocket_data.h"

#define MAX_CLIENT_PAYLOAD 128

#define RING_BUF_EMPTY 0
#define RING_BUF_FULL  1

typedef struct {
    char buf[MAX_CLIENT_PAYLOAD];
    int  len;
    int status;
}Ring_Buf;
#define MAX_RING_BUFFER_CNT  100

Ring_Buf  ring_rx[MAX_RING_BUFFER_CNT];
Ring_Buf  ring_tx[MAX_RING_BUFFER_CNT];

static WebSock_Status server_state = Socket_Init;
static struct lws_context *g_lws_context;

int callback_cmd_server(struct lws *wsi, enum lws_callback_reasons reason, 
void *user,
	      void *in, size_t len)
{
	int n;

	switch (reason) {

	/* when the callback is used for client operations --> */
    case LWS_CALLBACK_HTTP:
        lwsl_notice("Callback LWS_CALLBACK_HTTP\n");
        break;        
    case LWS_CALLBACK_WSI_CREATE:
        lwsl_notice("Create LWS_CALLBACK_WSI_CREATE\n");
        break;        
    case LWS_CALLBACK_WSI_DESTROY:
        lwsl_notice("Destory LWS_CALLBACK_WSI_DESTROY\n");
        break;
//    case LWS_CALLBACK_PROTOCOL_INIT:
    case LWS_CALLBACK_PROTOCOL_DESTROY:
        lwsl_notice("Protocal Destory LWS_CALLBACK_PROTOCOL_DESTROY\n");
        break;
	case LWS_CALLBACK_CLOSED:
		lwsl_notice("Close LWS_CALLBACK_CLOSED\n");
		server_state = Socket_Close;
		break;

	case LWS_CALLBACK_ESTABLISHED:
		lwsl_notice("Connect LWS_CALLBACK_ESTABLISHED\n");
		server_state = Socket_Connected;
		break;

	case LWS_CALLBACK_RECEIVE:
		lwsl_notice("Client RX: Size %d  \n %s \n", len , (char*)in);
        for(int i = 0; i < MAX_RING_BUFFER_CNT;i++){

            if(RING_BUF_EMPTY == ring_rx[i].status ){
                if(len <= MAX_CLIENT_PAYLOAD){
                    memcpy(&ring_rx[i].buf[LWS_PRE], in, len);
                    ring_rx[i].len = len;
                    ring_rx[i].status = RING_BUF_FULL;
                    break;
                }else{
                    HAL_ERR("(%s:%d) buffer two small %d, recieve data %d",
                        __FUNCTION__,__LINE__,RING_BUF_FULL, (int)len);
                }
            }
        }
		break;

	case LWS_CALLBACK_SERVER_WRITEABLE:
		/* we will send our packet... */
        for(int i = 0; i < MAX_RING_BUFFER_CNT;i++){
            if(RING_BUF_FULL == ring_tx[i].status){
                lwsl_notice("Client starting to send data\n");
                HAL_INFO("The Data is:\n %s \n" , &ring_tx[i].buf[LWS_PRE]);
		        n = lws_write(wsi, &ring_tx[i].buf[LWS_PRE], ring_tx[i].len, LWS_WRITE_TEXT);
                ring_tx[i].len = 0;
                ring_tx[i].status = RING_BUF_EMPTY;
            }
        }
        
        lws_callback_on_writable(wsi);
		break;       
	case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
		lwsl_notice("LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: len %d\n",
			    len);
		for (n = 0; n < (int)len; n++)
			lwsl_notice(" %d: 0x%02X\n", n,
				    ((unsigned char *)in)[n]);
		break;

	default:
		break;
	}

	return 0;
}

/* list of supported protocols and callbacks */
static struct lws_protocols protocols[] = {
	{
		FISH_PROTOCOL_NAME,
//        "http-only",
		callback_cmd_server,
		0,
		MAX_CLIENT_PAYLOAD,
	},
	{ NULL, NULL, 0, 0 } /* end */
};

static const struct lws_extension exts[] = {
	{
		"permessage-deflate",
		lws_extension_callback_pm_deflate,
		"permessage-deflate; client_max_window_bits"
	},
	{
		"deflate-frame",
		lws_extension_callback_pm_deflate,
		"deflate_frame"
	},
	{ NULL, NULL, NULL /* terminator */ }
};

char peer_name[128];
char peer_ip[128];
int websocket_server_create(const char *if_name , int port)
{
	struct lws_context_creation_info info;
	struct lws_client_connect_info client;
    int ietf_version = -1;
    char ads_port[128];
	int debug_level = LLL_ERR | LLL_WARN|LLL_NOTICE;
#ifndef _WIN32
	int syslog_options = LOG_PID | LOG_PERROR;
#endif

    memset(&info, 0, sizeof(info));
    memset(&client, 0, sizeof(client));

    /* tell the library what debug level to emit and to send it to syslog */
	/* we will only try to log things according to our debug_level */
#ifndef _WIN32
	setlogmask(LOG_UPTO (LOG_DEBUG));
	openlog("lwsts", syslog_options, LOG_DAEMON);
#endif
    /*
     * create the websockets context.  This tracks open connections and
     * knows how to route any traffic and which protocol version to use,
     * and if each connection is client or server side.
     *
     * For this client-only demo, we tell it to not listen on any port.
     */
    lws_set_log_level(debug_level, lwsl_emit_syslog);

    lwsl_notice("Built to support server operations\n");
#ifdef _WIN32
    info.iface = NULL;//if_name;
#else
#endif
    info.port = port;
	info.protocols = protocols;
    info.max_http_header_pool = 16;
	info.gid = -1;
	info.uid = -1;
    info.extensions = exts;
    info.options |= LWS_SERVER_OPTION_VALIDATE_UTF8;
    info.timeout_secs = 5;
	info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
			       "ECDHE-RSA-AES256-GCM-SHA384:"
			       "DHE-RSA-AES256-GCM-SHA384:"
			       "ECDHE-RSA-AES256-SHA384:"
			       "HIGH:!aNULL:!eNULL:!EXPORT:"
			       "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
			       "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
			       "!DHE-RSA-AES128-SHA256:"
			       "!AES128-GCM-SHA256:"
			       "!AES128-SHA256:"
			       "!DHE-RSA-AES256-SHA256:"
			       "!AES256-GCM-SHA384:"
			       "!AES256-SHA256";

	g_lws_context = lws_create_context(&info);
	if (g_lws_context == NULL) {
		lwsl_err("libwebsocket init failed\n");
		fprintf(stderr, "Creating libwebsocket context failed\n");
		return HAL_ERROR;
	}

    server_state = Socket_Created;

    return HAL_SUCCESS;
}

int websocket_server_destory()
{
	lwsl_notice("libwebsockets  Server exited cleanly\n");
	lws_context_destroy(g_lws_context);    

    server_state = Socket_Init;

    return HAL_SUCCESS;
}

int websocket_server_active_rw_action()
{
    int ret;

    ret = lws_service(g_lws_context, 1000);
    
    return HAL_SUCCESS;
}
/*
return the read data length
*/
int  websocket_server_read_data(char *p_buf, int len)
{
    int i ;
    for(i = 0; i < MAX_RING_BUFFER_CNT ; i++){
        if(RING_BUF_FULL == ring_rx[i].status)
            break;
    }

    /*no data in read qeue*/
    if(i == MAX_RING_BUFFER_CNT)
        return 0;

    if(len < ring_rx[i].len){
        HAL_ERR("(%s:%d) Data len %d ,Read buffer %d. No Enough Buffer for Read Data.\n",
            __FUNCTION__,__LINE__ , ring_rx[i].len , len);
        return HAL_NO_MEMORY;
    }

    memcpy(p_buf, &ring_rx[i].buf[LWS_PRE], ring_rx[i].len);
    ring_rx[i].status = RING_BUF_EMPTY;

    return ring_rx[i].len;

}

int websocket_server_write_data(char *p_buf, int len)
{
    int i ;
    for(i = 0; i < MAX_RING_BUFFER_CNT ; i++){
        if(RING_BUF_EMPTY == ring_tx[i].status)
            break;
    }

    if(i == MAX_RING_BUFFER_CNT){
        HAL_ERR("(%s:%d) Write Queue Full, please Waiting.\n",
            __FUNCTION__,__LINE__);
        return HAL_QUEUE_FULL;
    
    } 
    if(len > MAX_CLIENT_PAYLOAD){
        HAL_ERR("(%s:%d) Data len %d . No Enough Buffer for Write Data.\n",
            __FUNCTION__,__LINE__ , len);
        return HAL_NO_MEMORY;
    }
    memcpy(&ring_tx[i].buf[LWS_PRE], p_buf, len);
    ring_tx[i].len = len;
    ring_tx[i].status = RING_BUF_FULL;

    if(Socket_Connected == server_state)
        lws_callback_on_writable_all_protocol(g_lws_context, &protocols[0]);

    return HAL_SUCCESS;
}

WebSock_Status websocket_server_get_status()
{
    return server_state;
}



